package com.yunda.nebula.gateway.client.cloud.init;

import com.yunda.nebula.gateway.client.cloud.annotation.NebulaSpringCloudClient;
import com.yunda.nebula.gateway.client.cloud.config.NebulaSpringCloudConfig;
import com.yunda.nebula.gateway.client.cloud.dto.SpringCloudRegisterDTO;
import com.yunda.nebula.gateway.client.cloud.utils.OkHttpTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Controller;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
public class SpringCloudClientBeanPostProcessor implements BeanPostProcessor {
    
    private final ThreadPoolExecutor executorService;
    
    private final String url;

    private final NebulaSpringCloudConfig config;
    
    private final Environment env;

    private String contextPath = null;

    /**
     * Instantiates a new Soul client bean post processor.
     *
     * @param config the soul spring cloud config
     * @param env    the env
     */
    public SpringCloudClientBeanPostProcessor(final NebulaSpringCloudConfig config, final Environment env) {
        String adminUrl = config.getAdminUrl();
        String appName = env.getProperty("spring.application.name");
        String contextpath = env.getProperty("server.servlet.context-path");
        if(StringUtils.isEmpty(contextpath)){
            this.contextPath = contextpath;
        }
        if (adminUrl == null || "".equals(adminUrl)
                || appName == null || "".equals(appName)) {
            throw new RuntimeException("spring cloud param must config the adminUrl and appName");
        }
        this.config = config;
        this.env = env;
        this.url = adminUrl + "/soul-client/springcloud-register";
        executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
    }
    
    @Override
    public Object postProcessBeforeInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
        Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
        RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
        if (controller != null || restController != null || requestMapping != null) {
            String contextPath = config.getContextPath();
            //首先
            String[] prePath = requestMapping.path();
            NebulaSpringCloudClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), NebulaSpringCloudClient.class);
//            if (Objects.nonNull(clazzAnnotation)){
//                clazzAnnotation.enabled()
//            }
            //            if (Objects.nonNull(clazzAnnotation)) {
//                if (clazzAnnotation.path().indexOf("*") > 1) {
//                    String finalPrePath = prePath;
//                    executorService.execute(() -> post(buildJsonParams(clazzAnnotation, contextPath, finalPrePath)));
//                    return bean;
//                }
//                prePath = clazzAnnotation.path();
//            }
            final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
            for (Method method : methods) {
                NebulaSpringCloudClient nebulaSpringCloudClient = AnnotationUtils.findAnnotation(method, NebulaSpringCloudClient.class);
                String[] path = getPath(method);
                if (path == null || path.length == 0){
                    continue;
                }
                if (Objects.nonNull(nebulaSpringCloudClient)) {
                    executorService.execute(() -> post(buildJsonParams(nebulaSpringCloudClient, contextPath, null)));
                }
            }
        }
        return bean;
    }

    private String[] getPath(Method method) {
        return null;
    }

    private void post(final String json) {
        try {
            String result = OkHttpTools.getInstance().post(url, json);
            if (Objects.equals(result, "success")) {
                log.info("http client register success :{} " + json);
            } else {
                log.error("http client register error :{} " + json);
            }
        } catch (IOException e) {
            log.error("cannot register gateway admin param :{}", url + ":" + json);
        }
    }
    
    private String buildJsonParams(final NebulaSpringCloudClient nebulaSpringCloudClient, final String contextPath, final String prePath) {
        String appName = env.getProperty("spring.application.name");
        String path = contextPath + prePath + nebulaSpringCloudClient.path();
        String desc = nebulaSpringCloudClient.desc();
        String configRuleName = nebulaSpringCloudClient.ruleName();
        String ruleName = ("".equals(configRuleName)) ? path : configRuleName;
        SpringCloudRegisterDTO registerDTO = SpringCloudRegisterDTO.builder()
                .context(contextPath)
                .appName(appName)
                .path(path)
                .pathDesc(desc)
                .rpcType(nebulaSpringCloudClient.rpcType())
                .enabled(nebulaSpringCloudClient.enabled())
                .ruleName(ruleName)
                .build();
        return OkHttpTools.getInstance().getGosn().toJson(registerDTO);
    }
}


